from inspect import Parameter, signature
from typing import Callable, Dict, Type
from . import check


def from_dict_for_class_annotation(cls: Type, data_dict: Dict) -> object:
    """
    从字典中初始化类的实例
    """
    instance = cls()
    for key, value in data_dict.items():
        # 只对类注解中定义了的属性
        if key in cls.__annotations__ and not hasattr(instance, key):
            setattr(instance, key, value)
    return instance


def to_dict_for_class_annotation(self) -> Dict:
    """
    将类的实例转换为字典
    """
    return {key: value for key, value in self.__dict__.items() if key in self.__annotations__}


def from_dict_for_all_attributes(cls: Type, data_dict: Dict) -> object:
    """
    从字典中初始化类的实例，直接设置所有属性，不考虑类注解
    """
    instance = cls()
    for key, value in data_dict.items():
        setattr(instance, key, value)
    return instance


def to_dict_for_all_attributes(self) -> Dict:
    """
    将类的实例转换为字典，包含所有属性，不考虑类注解
    """
    return self.__dict__


def dict_class(from_dict: bool = True, to_dict: bool = True, only_class_annotation: bool = True) -> Callable:
    def decorator(cls: Type) -> Type:
        if from_dict:
            # 校验 __init__ 方法的参数类型
            sig = signature(cls.__init__)
            for param_name, param in sig.parameters.items():
                if param_name == "self":
                    continue
                param_type = param.annotation
                # 检查参数类型是否是 typing.Optional 或 typing.Any 或有默认值
                if param.default is Parameter.empty or not check.is_typing_optional_or_any(param_type):
                    raise TypeError(
                        f"Parameter '{param_name}' of __init__ must be of type typing.Optional, typing.Any, or have a default value"
                    )
            setattr(
                cls,
                "from_dict",
                classmethod(from_dict_for_class_annotation if only_class_annotation else from_dict_for_all_attributes),
            )
        if to_dict:
            setattr(
                cls,
                "to_dict",
                to_dict_for_class_annotation if only_class_annotation else to_dict_for_all_attributes,
            )
        return cls

    return decorator
